/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <zephyr/shell/shell.h>
#include <zephyr/shell/shell_uart.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/device.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/sys/reboot.h>
/*********************************************************************
 * 需要的标志定义
 *********************************************************************/
#define CHAR_1 0x18
#define CHAR_2 0x11
#define DOWNLOAD_IDLE 0
#define DOWNLOAD_BOOT 1
#define DOWNLOAD_APP  2
#define DOWNLOAD_DEST 3
#define APP_OFFSET_ADDR       0x00020000

/*********************************************************************
 * 全局变量与外部变量声明
 *********************************************************************/
extern struct k_timer wait_update_timer;
extern k_tid_t download_tid;
extern uint8_t download_type;
extern uint32_t FLASH_BASE_ADDR;
/*********************************************************************
 * 外部函数声明
 *********************************************************************/
void sys_reboot	(int type);
void jump_to_addr(uint32_t addr);
void download_function(void *p1, void *p2, void *p3);
void jump_to_boot(void);

/*********************************************************************
 * shell旁路开通
 *********************************************************************/
static int set_bypass(const struct shell *sh, shell_bypass_cb_t bypass)
{
	static bool in_use;

	if (bypass && in_use) {
		shell_error(sh, "Sample supports setting bypass on single instance.");

		return -EBUSY;
	}

	in_use = !in_use;
	if (in_use) {
		shell_print(sh, "Bypass started, press ctrl-x ctrl-q to escape");
		in_use = true;
	}

	shell_set_bypass(sh, bypass);

	return 0;
}

static void bypass_cb(const struct shell *sh, uint8_t *data, size_t len)
{
	static uint8_t tail;
	bool escape = false;

	/* Check if escape criteria is met. */
	if (tail == CHAR_1 && data[0] == CHAR_2) {
		escape = true;
	} else {
		for (int i = 0; i < (len - 1); i++) {
			if (data[i] == CHAR_1 && data[i + 1] == CHAR_2) {
				escape = true;
				break;
			}
		}
	}

	if (escape) {
		shell_print(sh, "Exit bypass");
		set_bypass(sh, NULL);
		tail = 0;
		return;
	}

	/* Store last byte for escape sequence detection */
	tail = data[len - 1];

	/* Do the data processing. */
	for (int i = 0; i < len; i++) {
		shell_fprintf(sh, SHELL_INFO, "%02x ", data[i]);
	}
	shell_fprintf(sh, SHELL_INFO, "| ");

	for (int i = 0; i < len; i++) {
		shell_fprintf(sh, SHELL_INFO, "%c", data[i]);
	}
	shell_fprintf(sh, SHELL_INFO, "\n");

}

static int cmd_bypass(const struct shell *sh, size_t argc, char **argv)
{
	return set_bypass(sh, bypass_cb);
}

SHELL_CMD_ARG_REGISTER(bypass, NULL, "Bypass shell", cmd_bypass, 1, 0);



/*********************************************************************
 * shell下载任务
 *********************************************************************/
static int cmd_download(const struct shell *shell, size_t argc, char **argv)
{
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);

	shell_print(shell, "download app to rom");
	k_sleep(K_MSEC(1));
	download_type = DOWNLOAD_APP;
	k_thread_start(download_tid);
	k_thread_resume(download_tid);
	return 0;
}

SHELL_CMD_REGISTER(download, NULL, "download app rom", cmd_download);

/*********************************************************************
 * shell reinit和uninit
 *********************************************************************/
void shell_uninit_download_cb(const struct shell *shell, int res)
{
	__ASSERT_NO_MSG(res >= 0);
	const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
	uart_irq_tx_disable(dev);
	uart_irq_rx_disable(dev);
}

void shell_init_from_work(struct k_work *work)
{
	const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
	bool log_backend = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > 0;
	uint32_t level =
		(CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > LOG_LEVEL_DBG) ?
		CONFIG_LOG_MAX_LEVEL : CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL;

	shell_init(shell_backend_uart_get_ptr(), dev,
		   shell_backend_uart_get_ptr()->ctx->cfg.flags,
		   log_backend, level);
}

void shell_reinit_trigger(void)
{
	static struct k_work shell_init_work;

	k_work_init(&shell_init_work, shell_init_from_work);
	int err = k_work_submit(&shell_init_work);

	(void)err;
	__ASSERT_NO_MSG(err >= 0);
}

static void stop_jump_app_handle(const struct device *dev, void *user_data)
{
	static uint8_t buf[1];
	static bool tx_busy;

	uart_irq_update(dev);

	if (uart_irq_rx_ready(dev)) {
		while (uart_fifo_read(dev, buf, sizeof(buf))) {
			if (!tx_busy) {
				uart_irq_tx_enable(dev);
			}
		}
	}

	if (uart_irq_tx_ready(dev)) {
		if (!tx_busy) {
			(void)uart_fifo_fill(dev, buf, sizeof(buf));
			tx_busy = true;
		} else {
			tx_busy = false;
			uart_irq_tx_disable(dev);
			if (buf[0] == 27) { //按下Esc停止跳转与开启shell
				uart_irq_rx_disable(dev);
				shell_reinit_trigger();
				k_timer_stop(&wait_update_timer);
			}
		}
	}

}

void boot_shell_uninit_cb(const struct shell *shell, int res)
{
	__ASSERT_NO_MSG(res >= 0);
	const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
		/* connect uart to my handler */
	uart_irq_callback_user_data_set(dev, stop_jump_app_handle, NULL);
	uart_irq_rx_enable(dev);
}

/*********************************************************************
 * shell 跳转app任务
 *********************************************************************/
static int cmd_go_jump_app(const struct shell *shell, size_t argc, char **argv)
{
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);

	k_sleep(K_MSEC(10));

	shell_print(shell, "go jump to app");
	jump_to_addr(FLASH_BASE_ADDR + APP_OFFSET_ADDR);

	return 0;
}

SHELL_CMD_REGISTER(go, NULL, "jump to app", cmd_go_jump_app);

/*********************************************************************
 * shell重启任务
 *********************************************************************/
static int cmd_reboot(const struct shell *shell, size_t argc, char **argv)
{
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);
	sys_reboot(SYS_REBOOT_COLD);
	return 0;
}

SHELL_CMD_REGISTER(reboot, NULL, "reboot", cmd_reboot);